begin
  execute immediate 'drop type m4o_context force';
exception
  when others then null;
end;
/
create or replace type m4o_context as object (

  -- ===========================================================================
  v_name         varchar2(100),
  v_process_step varchar2(4000),

  -- ===========================================================================
  -- create a new m4o_context within procedure/function i_proc. i_arg_xx
  -- are the input parameters of i_proc.
  constructor function m4o_context(
    self      in out nocopy m4o_context,
    i_name    in varchar2)
return self as result,
  -- ===========================================================================
  -- document the following program sequence, e.g.
  --
  -- declare
  --   v_pc m4o_context := new m4o_context;
  --   v_foo foo%rowtype;
  -- begin
  --   ...
  --   v_pc.do('Load foo data');
  --   select *
  --   bulk collect into v_foo
  --   from foo;
  --   ...
  -- end;
  member procedure do(
    self           in out nocopy m4o_context,
    i_process_step in varchar2),
  -- ===========================================================================
  -- write text to the log, without setting the process step.
  member procedure log(
    self     in out nocopy m4o_context,
    i_str    in varchar2),
  -- ===========================================================================
  -- log sql%rowcount
  member procedure log_rowcount,
  -- ===========================================================================
  -- raise error.
  member procedure error(i_error_text in varchar2 := null),
  -- ===========================================================================
  -- ensure that i_condition is true, otherwise raise error.
  member procedure assert(
    self         in out nocopy m4o_context,
    i_condition  in boolean,
    i_error_text in varchar2 := null),
  -- ===========================================================================
  -- ensure that i_var_value is not null, otherwise raise error that 
  -- variable i_var_name is null.
  member procedure required(
    self        in out nocopy m4o_context,
    i_var_value in varchar2,
    i_var_name  in varchar2),
  -- ===========================================================================
  -- Ensure that sql%rowcount is 1, otherwise raise error. 
  -- You can override the default error text with i_error_text.
  member procedure assert_one_row_changed(
    self         in out nocopy m4o_context,
    i_error_text in varchar2 := null)
)
/
